package com.techsoul.module.system.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.techsoul.common.core.constant.ApiConstants;
import com.techsoul.common.core.constant.UserConstants;
import com.techsoul.common.core.exception.ParamException;
import com.techsoul.common.core.pojo.TreeSelect;
import com.techsoul.common.core.pojo.vo.UserVo;
import com.techsoul.common.core.util.ServletUtils;
import com.techsoul.common.core.util.bean.BeanUtils;
import com.techsoul.common.core.util.collection.ListUtils;
import com.techsoul.common.core.util.lang.StringUtils;
import com.techsoul.common.security.annotation.DataScope;
import com.techsoul.common.security.service.TokenService;
import com.techsoul.common.security.util.SecurityUtils;
import com.techsoul.module.system.mapper.MenuMapper;
import com.techsoul.module.system.pojo.entity.Menu;
import com.techsoul.module.system.pojo.param.*;
import com.techsoul.module.system.pojo.vo.MetaVo;
import com.techsoul.module.system.pojo.vo.RouterVo;
import com.techsoul.common.core.pojo.vo.MenuVo;
import com.techsoul.module.system.service.IMenuService;
import com.techsoul.module.system.pojo.param.MenuSaveParam;
import com.techsoul.module.system.pojo.param.MenuSearchParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional(readOnly = true)
public class MenuServiceImpl implements IMenuService {

    @Autowired
    private MenuMapper mapper;

    @Autowired
    private TokenService tokenService;

    /**
     * 新增
     *
     * @param params
     * @return
     */
    @Transactional(readOnly = false)
    @Override
    public String insert(MenuSaveParam params) {
        UserVo user = tokenService.getLoginUser(ServletUtils.getRequest()).getUser();
        Menu entity = new Menu();
        BeanUtils.copyProperties(params, entity);
        entity.preInsert(user.getId());
        // 完善全节点信息
        if (StringUtils.isNotBlank(params.getPid()) && !StringUtils.equals("0",  params.getPid())) {
            Menu parent = this.mapper.selectById(params.getPid());
            entity.setTreeLevel(parent.getTreeLevel() + 1);
            String ancestorNames = parent.getMenuName();
            if (StringUtils.isNotBlank(parent.getAncestorNames())) {
                ancestorNames = parent.getAncestorNames() + "/" + parent.getMenuName();
            }
            String ancestorIds = parent.getId();
            if (StringUtils.isNotBlank(parent.getAncestorIds())) {
                ancestorIds = parent.getAncestorIds() + "," + parent.getId();
            }
            entity.setAncestor(ancestorIds, ancestorNames);
            entity.setMenuFullName(entity.getAncestorNames() + "/" + entity.getMenuName());
        } else {
            entity.setAncestor("", "");
            entity.setMenuFullName(entity.getMenuName());
        }
        this.mapper.insert(entity);
        return entity.getId();
    }

    /**
     * 删除
     *
     * @param id
     * @return
     */
    @Transactional(readOnly = false)
    @Override
    public Integer delete(String id) {
        return this.mapper.deleteById(id);
    }

    /**
     * 批量删除
     *
     * @param idList
     */
    @Transactional(readOnly = false)
    @Override
    public Integer batchDelete(List<String> idList) {
        if (ListUtils.isNotEmpty(idList)) {
            return this.mapper.deleteBatchIds(idList);
        } else {
            return 0;
        }
    }

    /**
     * 修改
     *
     * @param params
     * @return
     */
    @Transactional(readOnly = false)
    @Override
    public Integer update(MenuSaveParam params) {
        Menu entityFromDb = this.mapper.selectById(params.getId());
        if (entityFromDb == null) {
            throw new ParamException("id=" + params.getId());
        }
        UserVo user = tokenService.getLoginUser(ServletUtils.getRequest()).getUser();
        BeanUtils.copyProperties(params, entityFromDb);
        entityFromDb.preUpdate(user.getId());
        // 完善全节点信息
        if (StringUtils.isNotBlank(params.getPid()) && !StringUtils.equals("0",  params.getPid())) {
            Menu parent = this.mapper.selectById(params.getPid());
            entityFromDb.setTreeLevel(parent.getTreeLevel() + 1);
            String ancestorNames = parent.getMenuName();
            if (StringUtils.isNotBlank(parent.getAncestorNames())) {
                ancestorNames = parent.getAncestorNames() + "/" + parent.getMenuName();
            }
            String ancestorIds = parent.getId();
            if (StringUtils.isNotBlank(parent.getAncestorIds())) {
                ancestorIds = parent.getAncestorIds() + "," + parent.getId();
            }
            entityFromDb.setAncestor(ancestorIds, ancestorNames);
            entityFromDb.setMenuFullName(entityFromDb.getAncestorNames() + "/" + entityFromDb.getMenuName());
        } else {
            entityFromDb.setAncestor("", "");
            entityFromDb.setMenuFullName(entityFromDb.getMenuName());
        }
        return this.mapper.updateById(entityFromDb);
    }

    /**
     * 更新树形表信息
     */
    @Transactional(readOnly = false)
    @Override
    public void updateAll() {
        List<MenuVo> list = this.findAll(new MenuSearchParam());
        if (ListUtils.isNotEmpty(list)) {
            list.forEach(obj -> {
                MenuSaveParam saveParam = new MenuSaveParam();
                BeanUtils.copyBeanProp(saveParam, obj);
                this.update(saveParam);
            });
        }
    }

    /**
     * 详情
     *
     * @param id
     * @return
     */
    @Override
    public MenuVo findById(String id) {
        return this.mapper.findById(id);
    }

    /**
     * 列表
     *
     * @param params
     * @return
     */
    @DataScope(userAlias = "t2", orgAlias = "t3")
    @Override
    public List<MenuVo> findAll(MenuSearchParam params) {
        return this.mapper.findAll(params);
    }

    /**
     * 列表-分页
     *
     * @param params
     * @param current
     * @param pageSize
     * @return
     */
    @DataScope(userAlias = "t2", orgAlias = "t3")
    @Override
    public IPage<MenuVo> findAll(MenuSearchParam params, Integer current, Integer pageSize) {
        return this.mapper.findAll(new Page<>(current, pageSize), params);
    }

    /**
     * 树形结构数据
     * @param params
     * @return
     */
    @Override
    public List<MenuVo> findTree(MenuSearchParam params) {
        return this.buildTree(this.findUserMenus());
    }

    /**
     * 树形结构选择器数据
     * @param params
     * @return
     */
    @Override
    public List<TreeSelect> findTreeSelect(MenuSearchParam params) {
        List<MenuVo> menuVoList = this.mapper.findAll(params);
        return this.buildTreeSelect(menuVoList);
    }

    /**
     * 用户菜单列表
     *
     * @return
     */
    @Override
    public List<MenuVo> findUserMenus() {
        String userId = tokenService.getLoginUser(ServletUtils.getRequest()).getUser().getId();
        return this.findUserMenus(userId);
    }

    /**
     * 用户菜单列表
     *
     * @param userId 用户ID
     * @return
     */
    @Override
    public List<MenuVo> findUserMenus(String userId) {
        List<MenuVo> menuVoList = null;
        if (SecurityUtils.isAdmin(userId)) {
            menuVoList = this.mapper.findAll(new MenuSearchParam());
        } else {
            menuVoList = this.mapper.findUserMenus(userId);
        }
        return menuVoList;
    }

    /**
     * 用户菜单列表-树形结构
     *
     * @param userId 用户ID
     * @return
     */
    @Override
    public List<MenuVo> findUserMenuTree(String userId) {
        List<MenuVo> menuVoList = null;
        if (SecurityUtils.isAdmin(userId)) {
            menuVoList = this.mapper.findUserMenuForRouterAll();
        } else {
            menuVoList = this.mapper.findUserMenuForRouter(userId);
        }
        return this.genChildrens(menuVoList, "0");
    }

    /**
     * 用户权限集合
     *
     * @param userId 用户ID
     * @return
     */
    @Override
    public List<String> findUserPermissions(String userId) {
        return this.mapper.findUserPermissions(userId);
    }

    /**
     * 角色菜单列表
     *
     * @param roleId
     * @return
     */
    @Override
    public List<MenuVo> findRoleMenus(String roleId) {
        return this.mapper.findRoleMenus(roleId);
    }

    /**
     * 角色权限集合
     *
     * @param roleIdList
     * @return
     */
    @Override
    public List<String> findRolePermissions(List<String> roleIdList) {
        return this.mapper.findRolePermissions(roleIdList);
    }

    /**
     * 构建动态路由表
     *
     * @param menuVoList
     * @return
     */
    @Override
    public List<RouterVo> buildRouters(List<MenuVo> menuVoList) {
        List<RouterVo> routers = new LinkedList<>();
        for (MenuVo menuVo : menuVoList) {
            RouterVo router = new RouterVo();
            router.setModule(menuVo.getModule());
            router.setHidden(ApiConstants.N.equals(menuVo.getIsVisible()));
            router.setName(this.getRouteName(menuVo));
            router.setPath(this.getRouterPath(menuVo));
            router.setComponent(this.getComponentPath(menuVo));
            router.setMeta(new MetaVo(menuVo.getMenuName(), menuVo.getMenuIcon(), StringUtils.equals("1", menuVo.getIsCache())));
            List<MenuVo> childrenMenus = menuVo.getChildren();
            if (ListUtils.isNotEmpty(childrenMenus) && UserConstants.TYPE_DIRECTORY.equals(menuVo.getMenuType())) {
                // 有子菜单，且菜单类型为目录时，设置路由alwaysShow、redirect、children三个属性
                router.setAlwaysShow(true);
                router.setRedirect("noRedirect");
                router.setChildren(buildRouters(childrenMenus));
            }
            // 一级菜单，菜单类型为菜单，且不是外链时，当前路由有且仅有一个子路由，为菜单本身信息
            if ("0".equals(menuVo.getPid()) && UserConstants.TYPE_MENU.equals(menuVo.getMenuType()) && ApiConstants.N.equals(menuVo.getIsExternalLink())) {
                List<RouterVo> childrenList = new ArrayList<RouterVo>();
                RouterVo children = new RouterVo();
                children.setPath(menuVo.getRouterPath());
                children.setComponent(menuVo.getComponentPath());
                children.setName(StringUtils.capitalize(menuVo.getRouterPath()));
                children.setMeta(new MetaVo(menuVo.getMenuName(), menuVo.getMenuIcon(), StringUtils.equals("1", menuVo.getIsCache())));
                childrenList.add(children);
                router.setChildren(childrenList);
            }
            routers.add(router);
        }
        return routers;
    }


    /**
     * 获取路由名称
     *
     * @param menuVo
     * @return
     */
    public String getRouteName(MenuVo menuVo) {
        // 路由名称=大驼峰的路由路径
        String routerName = StringUtils.capitalize(menuVo.getRouterPath());
        // 一级菜单，菜单类型为菜单，且不是外链时，路由名称为空
        if ("0".equals(menuVo.getPid()) && UserConstants.TYPE_MENU.equals(menuVo.getMenuType()) && ApiConstants.N.equals(menuVo.getIsExternalLink())) {
            routerName = StringUtils.EMPTY;
        }
        return routerName;
    }

    /**
     * 获取路由地址
     *
     * @param menuVo
     * @return
     */
    public String getRouterPath(MenuVo menuVo) {
        String routerPath = menuVo.getRouterPath();
        // 一级菜单，菜单类型为目录，且不是外链时
        if ("0".equals(menuVo.getPid()) && UserConstants.TYPE_DIRECTORY.equals(menuVo.getMenuType()) && ApiConstants.N.equals(menuVo.getIsExternalLink())) {
            routerPath = "/" + menuVo.getRouterPath();
        }
        // 一级菜单，菜单类型为菜单，且不是外链时
        if ("0".equals(menuVo.getPid()) && UserConstants.TYPE_MENU.equals(menuVo.getMenuType()) && ApiConstants.N.equals(menuVo.getIsExternalLink())) {
            routerPath = "/";
        }
        return routerPath;
    }

    /**
     * 获取组件地址
     *
     * @param menuVo
     * @return
     */
    public String getComponentPath(MenuVo menuVo) {
        String component = UserConstants.LAYOUT;
        if (StringUtils.isNotBlank(menuVo.getComponentPath())) {
            component = menuVo.getComponentPath();
        }
        return component;
    }

    /**
     * 构建树形结构数据
     *
     * @param list
     * @return
     */
    @Override
    public List<MenuVo> buildTree(List<MenuVo> list) {
        List<MenuVo> returnList = new ArrayList<>();
        List<String> tempList = new ArrayList<>();
        // 获取所有的节点ID
        for (MenuVo node : list) {
            tempList.add(node.getId());
        }
        for (Iterator<MenuVo> iterator = list.iterator(); iterator.hasNext();) {
            MenuVo node = iterator.next();
            // 如果是顶级节点（父节点不在tempList里，则默认为顶级节点）, 遍历该父节点的所有子节点
            if (!tempList.contains(node.getPid())) {
                recursionGenChildren(list, node);
                returnList.add(node);
            }
        }
        if (returnList.isEmpty()) {
            returnList = list;
        }
        return returnList;
    }

    /**
     * 构建树形结构选择器数据
     *
     * @param list
     * @return
     */
    @Override
    public List<TreeSelect> buildTreeSelect(List<MenuVo> list) {
        List<MenuVo> tree = buildTree(list);
        return tree.stream().map(TreeSelect::new).collect(Collectors.toList());
    }

    /**
     * 递归生成父节点下的所有子节点
     *
     * @param menuVoList
     * @param pid
     * @return
     */
    private List<MenuVo> genChildrens(List<MenuVo> menuVoList, String pid) {
        List<MenuVo> childrenMenus = new ArrayList<>();
        // 遍历所有节点，返回父节点下的所有子节点
        for (Iterator<MenuVo> iterator = menuVoList.iterator(); iterator.hasNext(); ) {
            MenuVo menuVo = iterator.next();
            if (pid.equals(menuVo.getPid())) {
                // 递归生成所有节点的children
                this.recursionGenChildren(menuVoList, menuVo);
                childrenMenus.add(menuVo);
            }
        }
        return childrenMenus;
    }

    /**
     * 判断是否有子节点
     */
    private boolean hasChildren(List<MenuVo> list, MenuVo parent) {
        return getChildrens(list, parent).size() > 0 ? true : false;
    }

    /**
     * 获取子节点列表
     * @param list
     * @param parent
     * @return
     */
    private List<MenuVo> getChildrens(List<MenuVo> list, MenuVo parent) {
        List<MenuVo> childrens = new ArrayList<>();
        Iterator<MenuVo> iterator = list.iterator();
        while (iterator.hasNext()) {
            MenuVo children = iterator.next();
            if (StringUtils.isNotBlank(children.getPid()) && parent.getId().equals(children.getPid())) {
                childrens.add(children);
            }
        }
        return childrens;
    }

    /**
     * 递归生成父节点下所有节点的children
     * @param list
     * @param parent
     */
    private void recursionGenChildren(List<MenuVo> list, MenuVo parent) {
        List<MenuVo> childrenMenus = getChildrens(list, parent);
        parent.setChildren(childrenMenus);
        for (MenuVo menuVo : childrenMenus) {
            if (hasChildren(list, menuVo)) {
                recursionGenChildren(list, menuVo);
            }
        }
    }

}
